<?php

namespace CuiFox\yii\validators;

use yii\validators\Validator;

class IdCardValidator extends Validator
{
    /**
     * @param \yii\base\Model $model
     * @param string $attribute
     */
    public function validateAttribute($model, $attribute)
    {
        if (!$this->validateIdCard($model->$attribute)) {
            $this->addError($model, $attribute, '身份证号码无效');
        }
    }

    /**
     * 验证身份证号
     * @param $idCard
     * @return bool
     */
    private function validateIdCard($idCard)
    {
        $idCard = strtoupper($idCard);
        $pattern = '/(^\d{15}$)|(^\d{17}([0-9]|X)$)/';
        if (!preg_match($pattern, $idCard)) {
            return false;
        }

        $matches = [];

        // 检查15位
        if (strlen($idCard) == 15) {
            $pattern = '/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/';
            if (!preg_match($pattern, $idCard, $matches)) {
                return false;
            }

            // 验证生日
            $birth = "19" . $matches[2] . '/' . $matches[3] . '/' . $matches[4];
            if (!strtotime($birth)) {
                return false;
            }
        } else {// 检查18位
            $pattern = '/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/';
            if (!preg_match($pattern, $idCard, $matches)) {
                return false;
            }

            // 验证生日
            $birth = $matches[2] . '/' . $matches[3] . '/' . $matches[4];
            if (!strtotime($birth)) {
                return false;
            }

            // 检验18位身份证的校验码，校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
            $factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
            $verifyCodeList = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

            $sum = 0;
            for ($i = 0; $i < 17; $i++) {
                $sum += intval($idCard[$i]) * $factor[$i];
            }

            $mod = $sum % 11;
            $verifyCode = $verifyCodeList[$mod];

            if ($verifyCode != substr($idCard, 17, 1)) {
                return false;
            }
        }

        return true;
    }
}